package com.handydata.antifraud.rule;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.handydata.antifraud.bo.orderbaseinfo.OrderBaseInfoReqBO;
import com.handydata.antifraud.channel.outbound.DataPusher;
import com.handydata.antifraud.entity.RuleInfoDo;
import com.handydata.antifraud.port.RuleEngineInvokers;
import com.handydata.antifraud.to.RiskInputTO;
import com.handydata.antifraud.to.RiskMiddleTO;
import com.handydata.antifraud.to.RiskOutputTO;
import com.handydata.antifraud.util.HttpUtil;
import com.handydata.common.util.RedisKey;
import com.handydata.common.util.RedisUtil;

@Component
@Slf4j
public class RuleEngineScheduler {

    @Autowired
    private RuleEngineInvokerFactory ruleEngineInvokerFactory;

    @Autowired
    private ThreadPoolTaskExecutor ruleEngineThreadPool;

    @Autowired
    private DataPusher dataPusher;

    @Autowired
    private RedisUtil redisUtil;

    public static  Map<String,List<RuleInfoDo>> ruleTypeMap=new HashMap<>();

    @Value("${urule.resporityServerUrl}")
    private  String resporityServerUrl;

	public void  refeshRuleType() {
        List<String> content = null;
        try {
            log.info("开始刷新规则大类配置");
            content = IOUtils.readLines(HttpUtil.getFileStream(resporityServerUrl+"/urule/downloadFile?fileName=ruleType.json",10000));
            ruleTypeMap=JSON.parseObject(content.get(0),new TypeReference <Map<String,List<RuleInfoDo>>>(){});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Future<RiskOutputTO> runRuleEngine(OrderBaseInfoReqBO orderBaseInfoReqBO) {
        return  CompletableFuture.supplyAsync(() -> startRuleEngine(orderBaseInfoReqBO), ruleEngineThreadPool);
    }

    private ExecutorService newThreadPool(int corePoolSize, int maximumPoolSize, int capacity, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(capacity), threadFactory);
    }

    private ThreadFactory getNamedThreadFactory(String name) {
        return new ThreadFactoryBuilder()
                .setNameFormat(name + "-queue-thread-%d").build();
    }

    private void preDealData(JSONObject receiveData){
        try {
            String appList = receiveData.getString("appList");
            if (appList != null && appList.startsWith("[") && appList.endsWith("]")) {
                String strs = appList.replace("[", "").
                        replace("]", "").replaceAll(" ", "");
                if (strs.length() <= 0) {
                    receiveData.put("appList", null);
                } else {
                    receiveData.put("appList", new ArrayList<>(Arrays.asList(strs.split(","))));
                }
            }
            String totalMemoryCapacity = receiveData.getString("totalMemoryCapacity");
            if (totalMemoryCapacity != null && totalMemoryCapacity.length() > 0) {

                totalMemoryCapacity = totalMemoryCapacity.replaceAll(" ", "").replace("B", "");
                if (totalMemoryCapacity.indexOf("G") != -1) {
                    receiveData.put("totalMemoryCapacity", getDouble(totalMemoryCapacity.replace("G", "")) * 1024);
                } else {
                    receiveData.put("totalMemoryCapacity", getDouble(totalMemoryCapacity.replace("M", "")));
                }
            } else {
                receiveData.put("totalMemoryCapacity", 0);
            }
            String sdCardCapacity = receiveData.getString("sdCardCapacity");
            if (sdCardCapacity != null && sdCardCapacity.length() > 0) {
                sdCardCapacity = sdCardCapacity.replaceAll(" ", "").replace("B", "");
                if (sdCardCapacity.indexOf("G") != -1) {
                    receiveData.put("sdCardCapacity", getDouble(sdCardCapacity.replace("G", "")) * 1024);
                } else {
                    receiveData.put("sdCardCapacity", getDouble(sdCardCapacity.replace("M", "")));
                }
            } else {
                receiveData.put("sdCardCapacity", 0);
            }
            String dumpEnergy = receiveData.getString("dumpEnergy");
            if (dumpEnergy != null && dumpEnergy.length() > 0) {
                receiveData.put("dumpEnergy", getDouble(dumpEnergy.replaceAll(" ", "").toUpperCase().replace("MAH", "")));
            } else {
                receiveData.put("dumpEnergy", 0);
            }
            String batteryCapacity = receiveData.getString("batteryCapacity");
            if (batteryCapacity != null && batteryCapacity.length() > 0) {
                receiveData.put("batteryCapacity", getDouble(batteryCapacity.replaceAll(" ", "").toUpperCase().replace("MAH", "")));
            } else {
                receiveData.put("batteryCapacity", 0);
            }
			String mobileCapacity = receiveData.getString("mobileCapacity");
			if (mobileCapacity != null && mobileCapacity.length() > 0) {
				mobileCapacity = mobileCapacity.replaceAll(" ", "").replace("B", "");
				if (mobileCapacity.indexOf("G") != -1) {
					receiveData.put("mobileCapacity", getDouble(mobileCapacity.replace("G", "")) * 1024);
				} else {
					receiveData.put("mobileCapacity", getDouble(sdCardCapacity.replace("M", "")));
				}
			} else {
				receiveData.put("mobileCapacity", 0);
			}
            String batteryTemperature = receiveData.getString("batteryTemperature");
            if (batteryTemperature != null && batteryTemperature.length() > 0) {
                receiveData.put("batteryTemperature", getDouble(batteryTemperature.replaceAll(" ", "")));
            } else {
                receiveData.put("batteryTemperature", 0);
            }
        }catch (Exception ex){
            log.error("预处理订单数据出错: 订单业务数据{}  异常信息{}", receiveData, ex);
        }

    }
    private double getDouble(String val){
        if(val==null)
            return 0D;
        try {
            return  Double.valueOf(val);
        }catch (Exception ex){
            log.error("转换double 出错 {}",val);
        }
        return  0D;
    }

    @Autowired
    private RuleEngineInvokers ruleEngineInvokers;

    private RiskOutputTO startRuleEngine(OrderBaseInfoReqBO orderBaseInfoReqBO) {
        try {

            log.info("------startRuleEngine request orderId {},productCode {}-------------", orderBaseInfoReqBO.getOrderId(),orderBaseInfoReqBO.getProductCode());

            log.debug("startRuleEngine request orderId {}", orderBaseInfoReqBO);

            JSONObject receiveData = JSON.parseObject(orderBaseInfoReqBO.getRecieveData());

            preDealData(receiveData);

            RiskInputTO riskInputTO = copyProperties(receiveData, RiskInputTO.class);
            if(riskInputTO.getOrderId()==null||riskInputTO.getOrderId().length()<=0)
                riskInputTO.setOrderId(orderBaseInfoReqBO.getOrderId());
            RiskOutputTO riskOutputTO = new RiskOutputTO();
            RiskMiddleTO riskMiddleTO = new RiskMiddleTO();

			String custType = receiveData.getString("custTypeCode");
			if (custType == null) {
				custType = "";
			}
            String productCode=orderBaseInfoReqBO.getProductCode();
			String decisionScenario = receiveData.getString("decisionScenario");
			if (StringUtils.isEmpty(decisionScenario)) {
				decisionScenario = "";
			}
            RuleEngineInvoker invoker = ruleEngineInvokerFactory.getRuleEngineInvoker(this.sb(productCode, custType, decisionScenario).toString());

//            RuleEngineInvoker invoker = ruleEngineInvokerFactory.getRuleEngineInvoker(productCode, custType, decisionScenario);
			if (null == invoker) {
				log.error("未找到知识包  productCode {} custType {} decisionScenario{}", productCode, custType, decisionScenario);
				return null;
			} else {
				// 计算自然流量订单数
				Map map = JSONObject.parseObject(orderBaseInfoReqBO.getRecieveData());
        		if (StringUtils.equals("FLOW022", (String) map.get("custTypeCode"))) {
        			Map<String, Object> custTypeCodeMap = (Map<String, Object>)redisUtil.get(RedisKey.CUST_TYPE_CODE_KEY + "FLOW022");
        			if (custTypeCodeMap != null && custTypeCodeMap.get("FLOW022") != null) {
        				int num = Integer.valueOf(String.valueOf(custTypeCodeMap.get("FLOW022")));
        				riskInputTO.setCustypecodeCnt(num);
        			} else {
        				riskInputTO.setCustypecodeCnt(RedisKey.REDIS_NUM);
        			}
        		}
                invoker.invokeDecision(session -> {
					session.insert(riskInputTO);
					session.insert(riskMiddleTO);
					session.insert(riskOutputTO);
				});
				calFinalOut(riskInputTO, riskMiddleTO, riskOutputTO);
			}
            log.info("----------------end RuleEngine----------------------{}",orderBaseInfoReqBO.getOrderId());
            return riskOutputTO;
        }catch (Exception ex){
            log.error("startRuleEngine 报错{}",ex);
            return null;
        }

    }

    public StringBuffer sb (String productCode, String custType, String decisionScenario){
        StringBuffer sb = new StringBuffer();
        sb.append(productCode);
        sb.append(custType);
        sb.append(decisionScenario);
	    return sb;
    }
    //计算规则最终的高中低
    @SuppressWarnings({ "unchecked" })
	private void calFinalOut(RiskInputTO riskInputTO,RiskMiddleTO riskMiddleTO,RiskOutputTO outputTO){
        Map<String,List<RuleInfoDo>> map = (Map<String,List<RuleInfoDo>>)redisUtil.get(RedisKey.RULE_KNOWLEDGE);
        if(null == map){
            log.info("拿json文件失败");
            return;
        }
        log.info("开始聚合规则,聚合配置文件:{}",JSON.toJSON(ruleTypeMap));
        for(String ruleType:map.keySet()){
            log.info("开始计算大类{}",ruleType);
            List<RuleInfoDo> rules=map.get(ruleType);
            int risk=0;
            String riskDesc=null;
            for(RuleInfoDo ruleInfoDo :rules){
                log.info("开始计算大类下属性{}",JSON.toJSON(ruleInfoDo));
                Field f= ReflectionUtils.findField(riskMiddleTO.getClass(),ruleInfoDo.getOutProp());
                if(f!=null) {
                    log.info("找到该属性{}",JSON.toJSON(f));
                    f.setAccessible(true);
                    Object result=ReflectionUtils.getField(f, riskMiddleTO);
                    log.info("属性值{} {}",ruleInfoDo.getOutProp(),result);
                    if(result!=null){
                        Integer hit=Integer.valueOf(result.toString());
                        if(hit==1){
                            if("高".equals(ruleInfoDo.getRiskLevel())){
                                risk+=4;
                                log.info("risk 分数增加{}{}",4);
                            }else if("中".equals(ruleInfoDo.getRiskLevel())) {
                                risk+=2;
                                log.info("risk 分数增加{}",2);

                            }else if("低".equals(ruleInfoDo.getRiskLevel())){
                                risk+=1;
                                log.info("risk 分数增加{}",1);

                            }else if("暴力破解".equals(ruleInfoDo.getRiskLevel())){
                                risk+=-1;
                                log.info("risk 暴力破解 分数增加{}",-1);

                            }else if("代理设备".equals(ruleInfoDo.getRiskLevel())){
                                risk+=-4;
                                log.info("risk 代理设备分数增加{}",-4);

                            }
                        }
                    }
                }
            }
            log.info("risk 分数{}",risk);
            if(risk>=4){
                riskDesc="2";//4个低是一个高吗
            }else if(risk>=2){
                riskDesc="1";
            }else if(risk>=1){
                riskDesc="0";
            }else if(risk==-1){
                riskDesc="0";
            }else if(risk==-4){
                riskDesc="1";
            }
            Field f= ReflectionUtils.findField(outputTO.getClass(),ruleType);
            log.info("开始给大类赋值 field {} desc {}",JSON.toJSON(f),riskDesc);
            if(f!=null&&riskDesc!=null&&riskDesc.length()>0) {
                f.setAccessible(true);
                ReflectionUtils.setField(f, outputTO,riskDesc);
            }
        }

        if(outputTO.getGpsProvince()==null||outputTO.getGpsProvince().length()<=0){

          if(StringUtils.isEmpty(riskInputTO.getPhoneNum())&&StringUtils.isEmpty(riskInputTO.getBdLongitudeAndLatitude())){
              outputTO.setGpsProvince("无法解析");
          }else {
              outputTO.setGpsProvince("无法定位");
          }

      }
        if(StringUtils.isEmpty(outputTO.getGpsCity())){

            if(StringUtils.isEmpty(riskInputTO.getPhoneNum())&&StringUtils.isEmpty(riskInputTO.getBdLongitudeAndLatitude())){
                outputTO.setGpsCity("无法解析");
            }else {
                outputTO.setGpsCity("无法定位");
            }
        }
        if(StringUtils.isEmpty(outputTO.getRiskJob())){
            outputTO.setRiskJob("0");
        }
        if(StringUtils.isEmpty(outputTO.getTelBlackList())){
            outputTO.setTelBlackList("0");
        }
        if(StringUtils.isEmpty(outputTO.getGpsDistance())){
            outputTO.setGpsDistance("-1");
        }
        if(StringUtils.isEmpty(outputTO.getIsPlaceAllowed())){
            outputTO.setIsPlaceAllowed("1");
        }
        if(StringUtils.isEmpty(outputTO.getTelCity())){
            outputTO.setTelCity("无法解析");
        }
        if(StringUtils.isEmpty(outputTO.getTelProvince())){
            outputTO.setTelProvince("无法解析");
        }
        if("2".equals(outputTO.getTelBlackList())){
            outputTO.setTelBlackList("1");
        }
      outputTO.setOrderId(riskInputTO.getOrderId());
    }

    private <T> T copyProperties(Map<String, Object> map, Class<T> cls) {
        T obj = (T) BeanUtils.instantiateClass(cls);
        BeanWrapper beanWrapper = new BeanWrapperImpl(obj);
        beanWrapper.setPropertyValues((PropertyValues)(new MutablePropertyValues(map)),true,true);
        return obj;
    }
}
